#include "FLOW_core/flow_core_pch.h"
#include "FLOW_core/Mutex.h"
#include <windows.h>

#define MUTEX_MGR CORE::CMutexMgr::GetInstance( )

namespace CORE
{


	//=========================================================================

	/*!
	 * \brief Hidden class used to manage the mutexes without making dependencies with platform dependent types.
	 *
	 * Hidden class used to manage the mutexes without making dependencies with platform dependent types
	 * and allowing control over where and how the memory used for those platform dependent resources is
	 * allocated, this allows to reduce memory fragmentation.
	 */
	class CMutexMgr
	{
	private:
		//========================== DATA TYPE DEFINITIONS ====================

		//! typedef of the platform dependent mutex type
		typedef HANDLE	PDMutexType;

		//============================== DATA MEMBERS =========================

		//! function hash for CMutex*
		struct CMutexHasher
		{
			union vp
			{
				CMutex*	m_p;
				size_t	m_s;
			};

			size_t operator( ) ( CMutex* ap_mutex ) const
			{
				vp aux;
				aux.m_p = ap_mutex;
				return aux.m_s;
			}
		};

		//! hash map of mutexes
		typedef CHashMap< CMutex*, PDMutexType, CMutexHasher > TMutexHashMap;
		TMutexHashMap			m_hm_pd_mutex;

		//! Manager mutex used to avoid concurrency problems when creating and destroying the mutexes
		PDMutexType				m_mutex;

		//! Pointer to the unique instance of the mutex manager
		static CMutexMgr*		sg_p_mutex_mgr;

		//! Booleam that tells if the manager has been already initialized
		bool_t					m_b_initialized;

		//================================ METHODS ============================

		//! Default Constructor
		CMutexMgr( );

		//! Destructor
		~CMutexMgr( );

	public:

		//! Returns the unique instance of the mutex manager
		static CMutexMgr* GetInstance( );

		//! Creates a platform dependent mutex for the mutex passed as argument
		bool_t Create( CMutex* ap_mutex );

		//! Destroys the platform dependent mutex associated with the mutex passed as argument
		bool_t Destroy( CMutex* ap_mutex );

		//! Tries to get the ownership of the mutex passed
		void Lock( CMutex* ap_mutex );

		//! Releases the ownership of the mutex passed
		void Unlock( CMutex* ap_mutex );

		//! Initialized the mutex manager
		bool_t Initialize( void );

		//! Shutdowns the mutex manager and free resources
		bool_t Shutdown( void );
	};

	//=========================================================================

	CMutexMgr* CMutexMgr::sg_p_mutex_mgr = NULL;

	//=========================================================================

	CMutexMgr::CMutexMgr( )
		: m_b_initialized( false )
		, m_mutex( NULL )
	{
	}

	//=========================================================================

	CMutexMgr::~CMutexMgr( )
	{
		if( m_b_initialized )
			Shutdown( );
	}

	//=========================================================================

	bool_t CMutexMgr::Initialize( void )
	{
		//Create the mutex used to control creation and destruction of platform dependent mutexes
		m_mutex = CreateMutex( NULL, false, NULL );
		if( !m_mutex )
		{
			LPSTR p_msg = NULL;
			FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError( ), 0, p_msg, 0, NULL );
			FLOW_ASSERT( "Can't create the MutexMgr, internal mutex creation failed: %s", p_msg );
			return false;
		}
		m_b_initialized = true;

		return m_b_initialized;
	}

	//=========================================================================

	bool_t CMutexMgr::Shutdown( void )
	{
		m_b_initialized = false;
		WaitForSingleObject( m_mutex, INFINITE );
		//Free memory in the hash map
		TMutexHashMap( ).swap( m_hm_pd_mutex );
		ReleaseMutex( m_mutex );

		return CloseHandle( m_mutex ) != FALSE;
	}

	//=========================================================================

	CMutexMgr* CMutexMgr::GetInstance( void )
	{
		if( sg_p_mutex_mgr == NULL )
			sg_p_mutex_mgr = new CMutexMgr( );

		return sg_p_mutex_mgr;
	}

	//=========================================================================

	bool_t CMutexMgr::Create( CMutex* ap_mutex )
	{
		std::pair< TMutexHashMap::iterator, bool_t > result;
		bool_t b_ok = true;

		WaitForSingleObject( m_mutex, INFINITE );

			//result = m_hm_pd_mutex.insert( std::make_pair( ap_mutex, (PDMutexType)NULL ) );
			if( result.second )
			{
				result.first->second = CreateMutex( NULL, false, NULL ); // Create the mutex in the system
				if( !result.first->second )
					b_ok = false;
			}
		
		ReleaseMutex( m_mutex );

		return b_ok;
	}

	//=========================================================================

	bool_t CMutexMgr::Destroy( CMutex* ap_mutex )
	{
		TMutexHashMap::iterator it = m_hm_pd_mutex.find( ap_mutex );
		bool_t b_ok = true;

		if( it == m_hm_pd_mutex.end( ) )
			return false; //The mutex didn't existed

		//Lock the hashmap to remove the mutex
		WaitForSingleObject( m_mutex, INFINITE );
			b_ok = CloseHandle( it->second ) != FALSE;
			m_hm_pd_mutex.erase( it );
		ReleaseMutex( m_mutex );

		return b_ok;
	}

	//=========================================================================

	void CMutexMgr::Lock( CMutex* ap_mutex )
	{
		TMutexHashMap::iterator it = m_hm_pd_mutex.find( ap_mutex );

		if( it != m_hm_pd_mutex.end( ) )
			WaitForSingleObject( it->second, INFINITE );
	}

	//=========================================================================

	void CMutexMgr::Unlock( CMutex* ap_mutex )
	{
		TMutexHashMap::iterator it = m_hm_pd_mutex.find( ap_mutex );

		if( it != m_hm_pd_mutex.end( ) )
			ReleaseMutex( it->second );
	}

	//=========================================================================

	CMutex::CMutex( )
	{
		MUTEX_MGR->Create( this );
	}

	//=========================================================================

	CMutex::~CMutex( )
	{
		MUTEX_MGR->Destroy( this ); 
	}

	//=========================================================================

	void CMutex::Lock( void )
	{
		MUTEX_MGR->Lock( this );
	}

	//=========================================================================

	void CMutex::Unlock( void )
	{
		MUTEX_MGR->Unlock( this );
	}

	//=========================================================================

	bool_t CMutex::Initialize( void )
	{
		return MUTEX_MGR->Initialize( );
	}

	//=========================================================================

	bool_t CMutex::Shutdown( void )
	{
		return MUTEX_MGR->Shutdown( );
	}

	//=========================================================================

	CMutexLocker::CMutexLocker( CMutex& ar_mutex )
		: m_p_mutex( NULL )
	{
		m_p_mutex = &ar_mutex;
		m_p_mutex->Lock( );
	}

	//=========================================================================

	CMutexLocker::~CMutexLocker( )
	{
		if( m_p_mutex )
			m_p_mutex->Unlock( );
	}

	//=========================================================================

} //namespace CORE